{***************************************************************
    SpyGestion  Copyright (C) 2008 Christophe KOALI <nature2gas@gmail.com>
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
****************************************************************}
unit modBaseDeDonnesRapport;

interface

uses Classes, SysUtils,
     modBaseType,
     modGIDtoSTR,
     modCore,
     ESBDates,
     modStreamUtils,
     modRapport,

     ZlibEx,
     
     sFonctionsChaines;

(*****************************************************************************
  Objectif: Mémoriser les rapports pour pouvoir les retrouver
            Pouvoir mettre à jour les rapports
            Pouvoir acceder et mettre à jour facilement un rapport

            Pouvoir à partir d'une flotte, définir les planètes attaquable

update: une vraie base de données aurait été mieux vu la tournure du projet
  **************************************************************************)

const
   COLOR_DESIGN_BATIMENT = $00EAFFFE;   // jaune
   COLOR_DESIGN_RECHERCHE = $00FEFFF4;    // 00FDFFEA;  // bleu
   COLOR_DESIGN_VAISSEAUX = $00FFF4F9;   //00FFEAF3;  // violet
   COLOR_DESIGN_DEFENSE = $00F4F4FF;   // 00F2F2FF;   // rouge
   COLOR_DESIGN_RESPROD = $00F0F0F0;   // gris


   // Format original
   CNT_FORMATSAVE_VERSION1 = 1;

   // Format avec les flags ajoutés, ainsi que  l'historique des position de rapports
   CNT_FORMATSAVE_VERSION2  = 2;

   // Format avec l'univers et la galaxie inclu au debut de la base de donnees
   // ainsi que le numero de version de la base de donnees (ici 3 donc)
   CNT_FORMATSAVE_VERSION3  = 3;


   // Format avec le nombre de recycleur, et le score de la defense
   CNT_FORMATSAVE_VERSION4  = 4;


   CNT_LATEST_VESTION    = CNT_FORMATSAVE_VERSION4;


   CNT_RAPPORT_DB_FILE_FORMAT = 'Uni%d_Galaxie%d.od';

type




    TCollectionRapportHead = class(TList)
    private
       {--------------------------------------------------}
       {Elements spécialisés de la classes}
       FFichierHead: string;
       FFichierDB  : string;

       // Lors d'un changement de version (mise à jour), ce flag doit être activé
       FDBUpdating: boolean;

       FUnivers: integer;
       FGalaxie: integer;


       FLoadOnlyHeader: boolean;

       {--------------------------------------------------}
       {Gestion des pointeurs}
       // Utilisé pour la propriété Itemx
       function GetItemx(Index: integer): TRapportHead;

       // sauvegarde les éléments à sauvegarder dans le stream
       function SaveToStream(s: TStream): integer;
       function LoadFromStream(s: TStream): integer;
    public
       property FichierHeadSave: string read FFichierHead write FFichierHead;

       property Univers : integer read FUnivers write FUnivers;
       property Galaxie : integer read FGalaxie write FGalaxie;

       property LoadOnlyHeader: boolean read FLoadOnlyHeader write FLoadOnlyHeader;


       {--------------------------------------------------}
       {Gestion des pointeurs}
       // *** Ajout de pointeurs
       // Renvoi le pointeur de l'élément ajouté
       function Add(Element: TRapportHead): PRapportHead;



       // Simple ajout de pointeur
       procedure SimpleAdd(Pointeur: PRapportHead);
      
       // Renvoi le pointeur de l'élément ajouté/modifié
       // Utilise Get_Index pour vérifier si l'élément doit être mis à jour
       function Add_Update(Element: TRapportHead): PRapportHead;
      
      
       // *** Suppression de pointeur
       // Libère et supprime le pointeur Index
       // Renvoi True si réussit, False si hors limite ou erreur
       function Delete(Index: integer): boolean; overload;

       // Cherche Element avec Get_Index et supprime LE
       // PREMIER pointeur trouvé, renvoi True si réussit
       function Delete(Element: TPlanetLocation): boolean; overload;

      
       // *** Effacement de la liste de pointeur
       procedure Clear; override;
      
      
       // *** Requete sur la liste de pointeur
       function Get_Index(Element: TPlanetLocation): integer;
       function Get_Pointeur(Element: TPlanetLocation): PRapportHead;
       function Get_Item(Index: integer): TRapportHead;
       property Itemx[Index: integer]: TRapportHead read GetItemx;
      

      
       {--------------------------------------------------}
       {Procédures et fonctions spécialisées de la classes}
       // Destructeur
       destructor Destroy; override;
       constructor Create;
       
       function SaveToFile(Fichier: string): integer;
       function LoadFromFile(Fichier: string): integer;
       //function LoadFromFileGetNextValeur(var ChaineLu: string): string;


       // test effectué avant de charger une autre base de données
       function ExistSomeDataToSave: boolean;

       function LoadRapportDB(p: PRapportHead): integer;
       function LoadRapportFromDB(dbPosition: integer; var dst: string): integer;

       function AddUnknowFromCoord(Element: TPlanetLocation): PRapportHead;

       procedure Trie;
       procedure ResetIndex;

       // met le flag bShouldBeUpdateFixed à true de tous les éléments
       // ainsi que Position à -1
       // ceci pour permettre la conversion d'une base de données
       procedure PrepareFlagForFullSave;
    end;


    // Collection mais en tant que simple copie de pointeur
    // de planètes sondées récémment, et trie par quantité de GT à envoyer
    TCollectionRapportRecent = class(TList)
    private

    public
        procedure ImportRecent(src: TCollectionRapportHead; MaxDuree: integer);
        procedure ImportVachesAlaid(src: TCollectionRapportHead);

        procedure ImportFlag(src: TCollectionRapportHead; Flag: longword);

        procedure DeleteFlag(Flag: longword);

        procedure TrieParGT;
        procedure TrieDuree;
        procedure TrieDureeAttaque;
        procedure TrieFlottes;
        procedure TrieRecycleur;
        procedure TrieSDefenses;
        procedure TrieCoord;

        function Get_Pointeur(Element: TPlanetLocation): PRapportHead;
    end;


implementation

procedure TCollectionRapportHead.PrepareFlagForFullSave;
var pe: PRapportHead;
    i :integer;
begin

    if Count = 0 then exit;

    for i := 0 to Count - 1 do begin
        pe := Items[i];

        pe.Position := -1;
        pe.bShouldBeUpdateFixed := true;
    end;


end;

procedure TCollectionRapportRecent.DeleteFlag(Flag: longword);
var pe: PRapportHead;
    i :integer;
    Avant: longword;
begin

    if Count = 0 then begin
       exit;
    end;

    for i := 0 to Count - 1 do begin
       pe := Items[i];

       Avant := pe.Flags;
       sfcFlagsDel(Flag, pe.Flags);

       if pe.Flags <> Avant then begin
          pe.bShouldBeUpdateFixed := true;
       end;

    end;

end;

procedure TCollectionRapportRecent.ImportFlag(src: TCollectionRapportHead; Flag: longword);
var pe: PRapportHead;
    i :integer;
    CurrDate, LastDate: int64;
begin

    if src.Count = 0 then exit;

    for i := 0 to src.Count - 1 do begin
       pe := src.Items[i];

       if sfcFlagsIsIn(Flag, pe.Flags) then begin
          // On peut ajouter le pointeur
          Add(pe);
       end;

    end;

end;

procedure TCollectionRapportRecent.ImportVachesAlaid(src: TCollectionRapportHead);
begin
    ImportFlag(src, FLAG_POULE_AUX_OEUFS_DOR);
end;


procedure TCollectionRapportRecent.ImportRecent(src: TCollectionRapportHead; MaxDuree: integer);
var pe: PRapportHead;
    i :integer;
    CurrDate, LastDate: int64;
begin

    if src.Count = 0 then exit;

    CurrDate := DateTimeToUnix(Now);

    for i := 0 to src.Count - 1 do begin
       pe := src.Items[i];

       LastDate := mrGetMostRecentDate(pe.DateLastEspionnage);

       if LastDate > 100 then begin
          if CurrDate - LastDate < MaxDuree then begin
             // On peut ajouter le pointeur
             Add(pe);
          end;

       end;

    end;


end;



function brCompareElementsRecycleur(Item1, Item2: Pointer): Integer;
var e1, e2: PRapportHead;
    rc1, rc2: single;
begin

    e1 := Item1;
    e2 := Item2;




    // Nombre de recycleur pour tout récupérer
    rc1 := e1.RecyclageNbRessource / 20000;

    // Nombre de recycleur pour tout récupérer
    rc2 := e2.RecyclageNbRessource / 20000;





    if sfcFlagsIsIn(FLAG_A_RECYCLER, e1.Flags) then begin
       // e1 doit se faire recycler
       // Il doit etre mis en évidence

       if sfcFlagsIsIn(FLAG_A_RECYCLER, e2.Flags) then begin
          // e2 doit se faire recycler
          // il doit être mis en évidence
          // lors de e1 et e2 dépendra de la valeur de rc1 et rc2

       end else begin
          // e2 n'a pas le flag, donc e1 doit
          // Alors e1 doit être avant e2
          Result := -1;
          exit;

       end;


    end else if  sfcFlagsIsIn(FLAG_A_RECYCLER, e2.Flags) then begin
       // e2 doit se faire recycler
       // Il doit etre mis en évidence
       // et e1 n'a pas le flag
       // Alors e2 doit être avant e1
       Result := 1;
       exit;
    end;



    // Compare renvoie < 0 si Item1 est inférieur à Item2, donc si Item1 doit être placé avant Item2
    //                   0 s'ils sont égaux
    //                 > 0 si Item1 est supérieur à Item2, donc si Item1 doit être placé après Item2
    if rc1 < rc2 then
       // ATTENTION: on trie dans l'ordre décroissant
       // Ici, Item1 doit être placé après Item2
       Result := 1
    else if rc1 = rc2 then
       Result := 0
    else
       // ATTENTION: on trie dans l'ordre décroissant
       Result := -1;
       
end;


function brCompareElementsGT(Item1, Item2: Pointer): Integer;
var e1, e2: PRapportHead;
    gt1, gt2: single;
    TotalRessource: integer;
begin

    e1 := Item1;
    e2 := Item2;

    // Nombre de GT pour récupérer toute les ressources possible
    TotalRessource := e1.Ressource.Metal + e1.Ressource.Cristal + e1.Ressource.Deuterium;

    // On ne peut prendre au maximum la moitié des ressources
    gt1 := (TotalRessource div 2) / 25000;

    // Nombre de GT pour récupérer toute les ressources possible
    TotalRessource := e2.Ressource.Metal + e2.Ressource.Cristal + e2.Ressource.Deuterium;

    // On ne peut prendre au maximum la moitié des ressources
    gt2 := (TotalRessource div 2) / 25000;


    // Compare renvoie < 0 si Item1 est inférieur à Item2,
    //                   0 s'ils sont égaux
    //                 > 0 si Item1 est supérieur à Item2
    if gt1 < gt2 then
       // ATTENTION: on trie dans l'ordre décroissant
       Result := 1
    else if gt1 = gt2 then
       Result := 0
    else
       // ATTENTION: on trie dans l'ordre décroissant
       Result := -1;
       
end;

function brCompareElementsDureeAttaque(Item1, Item2: Pointer): Integer;
var e1, e2: PRapportHead;
    l1, l2: int64;
begin

    e1 := Item1;
    e2 := Item2;


    l1 := mrGetMostRecentDate(e1.DateLastAttaque);
    l2 := mrGetMostRecentDate(e2.DateLastAttaque);

    if (l1 < 100) then
       l1 := maxint;

    if (l2 < 100) then
       l2 := maxint;

    // Compare renvoie < 0 si Item1 est inférieur à Item2, (c'est à dire si item2 doit être placé après item1)
    //                   0 s'ils sont égaux
    //                 > 0 si Item1 est supérieur à Item2  (si Item2 doit être placé avant Item1)
    if l1  < l2 then
       // ATTENTION: on trie dans l'ordre décroissant
       Result := -1
    else if l1 = l2 then
       Result := 0
    else
       // ATTENTION: on trie dans l'ordre décroissant
       Result := 1;
       
end;

function brCompareElementsDuree(Item1, Item2: Pointer): Integer;
var e1, e2: PRapportHead;
    l1, l2: int64;
begin

    e1 := Item1;
    e2 := Item2;


    l1 := mrGetMostRecentDate(e1.DateLastEspionnage);
    l2 := mrGetMostRecentDate(e2.DateLastEspionnage);


    // Compare renvoie < 0 si Item1 est inférieur à Item2,
    //                   0 s'ils sont égaux
    //                 > 0 si Item1 est supérieur à Item2
    if l1  < l2 then
       // ATTENTION: on trie dans l'ordre décroissant
       // un grand nombre doit être placé devant
       Result := -1
    else if l1 = l2 then
       Result := 0
    else
       // ATTENTION: on trie dans l'ordre décroissant
       Result := 1;

end;


function brCompareElementsSDef(Item1, Item2: Pointer): Integer;
var e1, e2: PRapportHead;
    l1, l2: integer;
begin

    e1 := Item1;
    e2 := Item2;

    if not(scDefense in e1.SectionConnu) then begin
       l1 := -1;
    end else
       l1 := e1.DefenseScore;

    if not(scDefense in e2.SectionConnu) then begin
       l2 := -1;
    end else
       l2 := e2.DefenseScore;

    if l1 > l2 then
       // ATTENTION: on trie dans l'ordre décroissant
       // Un grand nombre doit être placé devant
       Result := -1
    else if l1 = l2 then
       Result := 0
    else
       // ATTENTION: on trie dans l'ordre décroissant
       Result := 1;

end;

function brCompareElementsFlotte(Item1, Item2: Pointer): Integer;
var e1, e2: PRapportHead;
    l1, l2: integer;
begin

    e1 := Item1;
    e2 := Item2;

    if not(scFlotte in e1.SectionConnu) then begin
       l1 := -1;
    end else
       l1 := e1.NbFlotte;


    if not(scFlotte in e2.SectionConnu) then begin
       l2 := -1;
    end else
       l2 := e2.NbFlotte;


    // Compare renvoie < 0 si Item1 est inférieur à Item2,
    //                   0 s'ils sont égaux
    //                 > 0 si Item1 est supérieur à Item2
    if l1 > l2 then
       // ATTENTION: on trie dans l'ordre décroissant
       // Un grand nombre doit être placé devant
       Result := -1
    else if l1 = l2 then
       Result := 0
    else
       // ATTENTION: on trie dans l'ordre décroissant
       Result := 1;

end;


procedure TCollectionRapportRecent.TrieSDefenses;
begin
   Sort(brCompareElementsSDef);
end;

procedure TCollectionRapportRecent.TrieFlottes;
begin
    Sort(brCompareElementsFlotte);
end;

procedure TCollectionRapportRecent.TrieRecycleur;
begin
    Sort(brCompareElementsRecycleur);
end;

procedure TCollectionRapportRecent.TrieDureeAttaque;
begin
    Sort(brCompareElementsDureeAttaque);
end;

procedure TCollectionRapportRecent.TrieDuree;
begin
    Sort(brCompareElementsDuree);
end;

procedure TCollectionRapportRecent.TrieParGT;
begin
    Sort(brCompareElementsGT);
end;

function brCompareElements(Item1, Item2: Pointer): Integer;
var e1, e2: PRapportHead;
begin

    // Compare renvoie < 0 si Item1 est inférieur à Item2,
    //                   0 s'ils sont égaux
    //                 > 0 si Item1 est supérieur à Item2
    // Voir http://alphomega.developpez.com/TriTList/
    // pour savoir comment trier sur plusieurs critére


    e1 := Item1;
    e2 := Item2;

    Result := trieCompareLocation(e1.PlaneteLocation, e2.PlaneteLocation);

end;

procedure TCollectionRapportHead.Trie;
begin
    Sort(brCompareElements);
end;

procedure TCollectionRapportRecent.TrieCoord;
begin
    Sort(brCompareElements);
end;



function TCollectionRapportHead.LoadRapportFromDB(dbPosition: integer; var Dst: string): integer;
var OutputStream: TMemoryStream;
    DeCompressionStream: TZDecompressionStream;

    FIn: TFileStream;
    TailleRap, r: integer;

begin
    Result := -1;
    Dst := '';


    if dbPosition < 0 then exit;
    if FileExists(FFichierDB) = false then exit;


    r := 0;
    try
      FIn := TFileStream.Create(FFichierDB, fmOpenRead);
    except
      r := -1;
      FIn.Free;
      FIn := nil;
    end;

    if FIn = nil then exit;

    // Se positionne dans le fichier
    FIn.Seek(dbPosition, soBeginning);

    // Lit la taille du rapport compressé
    r := FIn.Read(TailleRap, SizeOf(TailleRap));

    if r < SizeOf(TailleRap) then begin
       FIn.Free;
       Result := -2;
       exit;
    end;

    try
       // Stream non compressé
       OutputStream := TMemoryStream.Create;

       // Créé le stream qui va servir à décompresser
       DecompressionStream := TZDecompressionStream.Create(FIn);

       // Décompression
       OutputStream.CopyFrom(DecompressionStream, TailleRap);

       // Libère la classe de décompression
       DecompressionStream.Free;
       DecompressionStream := nil;

       // Charger à partir des données décompressées
       OutputStream.Seek(0, soBeginning);

       SetLength(Dst, OutputStream.Size);

       Result := OutputStream.Read(Dst[1], OutputStream.Size);
    finally
        // Libère le stream décompressé
        OutputStream.Free;

        if DecompressionStream <> nil then
           DecompressionStream.Free;

        FIn.Free;
    end;

end;


function TCollectionRapportHead.LoadRapportDB(p: PRapportHead): integer;
begin
    Result := -1;
    if p = nil then exit;
    Result := LoadRapportFromDB(p.PositionRapport, p.RapportFullOri);
end;

(*
function TCollectionRapportHead.LoadRapportDB(p: PRapportHead): integer;
var OutputStream: TMemoryStream;
    DeCompressionStream: TZDecompressionStream;

    FIn: TFileStream;
    TailleRap, r: integer;

begin
    Result := -1;

    if p = nil then exit;
    if p.PositionRapport < 0 then exit;
    if FileExists(FFichierDB) = false then exit;


    r := 0;
    try
      FIn := TFileStream.Create(FFichierDB, fmOpenRead);
    except
      r := -1;
      FIn.Free;
      FIn := nil;
    end;

    if FIn = nil then exit;

    // Se positionne dans le fichier
    FIn.Seek(p.PositionRapport, soBeginning);

    // Lit la taille du rapport compressé
    r := FIn.Read(TailleRap, SizeOf(TailleRap));


    try
       // Stream non compressé
       OutputStream := TMemoryStream.Create;

       // Créé le stream qui va servir à décompresser
       DecompressionStream := TZDecompressionStream.Create(FIn);

       // Décompression
       OutputStream.CopyFrom(DecompressionStream, TailleRap);

       // Libère la classe de décompression
       DecompressionStream.Free;
       DecompressionStream := nil;

       // Charger à partir des données décompressées
       OutputStream.Seek(0, soBeginning);

       SetLength(p.RapportFullOri, OutputStream.Size);

       Result := OutputStream.Read(p.RapportFullOri[1], OutputStream.Size);
    finally
        // Libère le stream décompressé
        OutputStream.Free;

        if DecompressionStream <> nil then
           DecompressionStream.Free;

        FIn.Free;
    end;



end;
*)

function LoadFixedData(s: TStream; p: PRapportHead; Version: integer = CNT_LATEST_VESTION): integer;
var r: integer;
    i: integer;
begin

    Result := -1;



    with p^ do begin
       r := s.Read(PlaneteLocation, SizeOf(PlaneteLocation));
       if r < SizeOf(PlaneteLocation) then exit;

       r := s.Read(Ressource, SizeOf(Ressource));
       if r < SizeOf(Ressource) then exit;

       r := s.Read(DateLastEspionnage, SizeOf(DateLastEspionnage));
       if r < SizeOf(DateLastEspionnage) then exit;

       r := s.Read(DateLastAttaque, SizeOf(DateLastAttaque));
       if r < SizeOf(DateLastAttaque) then exit;

       r := s.Read(NbDefense, SizeOf(NbDefense));
       if r < SizeOf(NbDefense) then exit;

       r := s.Read(NbFlotte, SizeOf(NbFlotte));
       if r < SizeOf(NbFlotte) then exit;

       r := s.Read(SectionConnu, SizeOf(SectionConnu));
       if r < SizeOf(SectionConnu) then exit;

       r := s.Read(NbEspionnage, SizeOf(NbEspionnage));
       if r < SizeOf(NbEspionnage) then exit;

       r := s.Read(NbAttack, SizeOf(NbAttack));
       if r < SizeOf(NbAttack) then exit;


       r := s.Read(PourcentContreEspion, SizeOf(PourcentContreEspion));
       if r < SizeOf(PourcentContreEspion) then exit;



       // Position du dernier rapport dans le fichier data
       r := s.Read(PositionRapport, SizeOf(PositionRapport));
       if r < SizeOf(PositionRapport) then exit;



       r := s.Read(Flags, SizeOf(Flags));
       if r < SizeOf(Flags) then exit;

       r := s.Read(VieuxRapport, SizeOf(VieuxRapport));
       if r < SizeOf(VieuxRapport) then exit;


       
       if Version > CNT_FORMATSAVE_VERSION3 then begin

         r := s.Read(RecyclageNbRessource, SizeOf(RecyclageNbRessource));
         if r < SizeOf(RecyclageNbRessource) then exit;

         r := s.Read(DefenseScore, SizeOf(DefenseScore));
         if r < SizeOf(DefenseScore) then exit;

       end else begin
         // Nombre de ressource à recycler
         RecyclageNbRessource  := 0;

         // Evaluation de la défense
         DefenseScore := 0;
       end;

    end;

    Result := 1;


end;


function SaveFixedData(s: TStream; p: PRapportHead): integer;
var r: integer;
begin
    Result := -1;

    with p^ do begin
       r := s.Write(PlaneteLocation, SizeOf(PlaneteLocation));
       if r < SizeOf(PlaneteLocation) then exit;

       r := s.Write(Ressource, SizeOf(Ressource));
       if r < SizeOf(Ressource) then exit;

       r := s.Write(DateLastEspionnage, SizeOf(DateLastEspionnage));
       if r < SizeOf(DateLastEspionnage) then exit;

       r := s.Write(DateLastAttaque, SizeOf(DateLastAttaque));
       if r < SizeOf(DateLastAttaque) then exit;

       r := s.Write(NbDefense, SizeOf(NbDefense));
       if r < SizeOf(NbDefense) then exit;

       r := s.Write(NbFlotte, SizeOf(NbFlotte));
       if r < SizeOf(NbFlotte) then exit;


       r := s.Write(SectionConnu, SizeOf(SectionConnu));
       if r < SizeOf(SectionConnu) then exit;


       r := s.Write(NbEspionnage, SizeOf(NbEspionnage));
       if r < SizeOf(NbEspionnage) then exit;

       r := s.Write(NbAttack, SizeOf(NbAttack));
       if r < SizeOf(NbAttack) then exit;


       r := s.Write(PourcentContreEspion, SizeOf(PourcentContreEspion));
       if r < SizeOf(PourcentContreEspion) then exit;


       // Position du dernier rapport dans le fichier data
       r := s.Write(PositionRapport, SizeOf(PositionRapport));
       if r < SizeOf(PositionRapport) then exit;


       // La sauvegarde se fait toujours avec la version la plus récente
       Flags := Flags and FLAGS_AND_BEFORE_SAVE;
       r := s.Write(Flags, SizeOf(Flags));
       if r < SizeOf(Flags) then exit;

       r := s.Write(VieuxRapport, SizeOf(VieuxRapport));
       if r < SizeOf(VieuxRapport) then exit;


       // version 4
       r := s.Write(RecyclageNbRessource, SizeOf(RecyclageNbRessource));
       if r < SizeOf(RecyclageNbRessource) then exit;

       r := s.Write(DefenseScore, SizeOf(DefenseScore));
       if r < SizeOf(DefenseScore) then exit;

    end;

    Result := 1;

end;

function SaveRapportToData(s: TStream; p: PRapportHead): integer;
var InputStream: TMemoryStream;
    CompressionStream: TZCompressionStream;
    PositionOrigine, PositionFinale: integer;
    lngFileCompress: integer;
begin


     PositionOrigine := s.Position;
     lngFileCompress := length(p.RapportFullOri);
     s.Write(lngFileCompress, SizeOf(lngFileCompress));

     // Avant de remplacer la position, sauvegarde l'ancienne
     mrInjectRapportPosition(p.VieuxRapport, p.PositionRapport);
     p.PositionRapport := PositionOrigine;

     try
        // Stream non compressé
        InputStream := TMemoryStream.Create;

        // Ecrit dans le stream
        InputStream.Write(p.RapportFullOri[1], length(p.RapportFullOri));
        InputStream.Seek(0, soBeginning);

        // Créé le stream qui va servir à compresser (compression maximale: zcMax)
        CompressionStream := TZCompressionStream.Create(s, zcMax);

        // Compresse le stream
        CompressionStream.CopyFrom(InputStream, InputStream.Size);

     finally
        // Libère la classe de compression
        CompressionStream.Free;

        // Libère le stream non compressé
        InputStream.Free;

     end;

  //   PositionFinale  := s.Position;
  //   lngFileCompress := PositionFinale - PositionOrigine - SizeOf(lngFileCompress);

  //   s.Seek(PositionOrigine, soBeginning);
  //   s.Write(lngFileCompress, SizeOf(lngFileCompress));
  //   s.Seek(0, soEnd);

end;

procedure TCollectionRapportHead.ResetIndex;
var i: integer;
    pe: PRapportHead;
begin
                      

end;

function TCollectionRapportHead.LoadFromStream(s: TStream): integer;
var Element: PRapportHead;
    i :integer;
    cCount: integer;
    dbVersion : integer;
begin



    // Charge le numero de version avant tout
    if s.Read(dbVersion, SizeOf(dbVersion)) < SizeOf(dbVersion) then begin
       Result := -1;
       exit;
    end;



    // Charge l univers et la galaxie
    if s.Read(FUnivers, SizeOf(FUnivers)) < SizeOf(FUnivers) then begin
       Result := -1;
       exit;
    end;

    if s.Read(FGalaxie, SizeOf(FGalaxie)) < SizeOf(FGalaxie) then begin
       Result := -1;
       exit;
    end;



    // On ne charge pas les rapports brut zippé
    if s.Read(cCount, SizeOf(cCount)) < SizeOf(cCount) then begin
       Result := -1;
       exit;
    end;

    Element := nil;

    if FLoadOnlyHeader then begin
       Result := 1;
       exit;
    end;

    SetCapacity(cCount);
    
    for i := 1 to cCount do begin
       if Element = nil then
          New(Element);


       Element.Position := s.Position;
       if LoadFixedData(s, Element, dbVersion) > 0 then begin

          Element.bShouldBeUpdateRapport := false;

          if dbVersion = CNT_LATEST_VESTION then begin
             Element.bShouldBeUpdateFixed   := false;

          end else begin
             // On devra tout mettre à jour, la taille de chaque enregistrement
             // ayant augmenté, chaque position changera
             Element.bShouldBeUpdateFixed   := true;
             Element.Position := -1;
             FDBUpdating := true;
          end;

          Element.RapportFullOri := '';

          SimpleAdd(Element);
          Element := nil;

       end else
          // fin du fichier alors
          break;

    end;

    if Element <> nil then
       Dispose(Element);

    Result := cCount;

    Trie;

end;

function TCollectionRapportHead.SaveToStream(s: TStream): integer;
var Element: PRapportHead;
    i :integer;
    FData: TFileStream;
    cCount: integer;
    dbVersion: integer;
begin

    if FDBUpdating then begin
       // ce flag doit avoir été remis à 0 avant l'appel de cette fonction
       Result := -77;
       exit;
    end;

    FData := nil;

    try
       if FileExists(FFichierDB) then
          FData := TFileStream.Create(FFichierDB, fmOpenWrite)
       else if FFichierDB <> '' then
          FData := TFileStream.Create(FFichierDB, fmCreate);

    except
       FData.Free;
       FData := nil;
    end;


    // se place au debut du fichier
    s.Seek(0, soBeginning);


    // Ecrit le numero de version avant tout
    dbVersion := CNT_LATEST_VESTION;
    s.Write(dbVersion, SizeOf(dbVersion));


    // Ecrit l univers et la galaxie
    s.Write(FUnivers, SizeOf(FUnivers));

    s.Write(FGalaxie, SizeOf(FGalaxie));


    // Puis ecrit le nombre d'élément
    cCount := Count;
    s.Write(cCount, SizeOf(cCount));




    if Count = 0 then begin
       Result := 0;

       if FData <> nil then begin
          FData.Free;
          FData := nil;
       end;

       exit;
    end;

    if FData = nil then begin
       Result := -1;
       exit;
    end;

    // Se place en fin de fichier
    FData.Seek(0, soEnd);



    for i := 0 to Count - 1 do begin
        Element := Items[i];

        if (Element.bShouldBeUpdateRapport) and (Element.RapportFullOri <> '') then begin
           SaveRapportToData(FData, Element);
           Element.bShouldBeUpdateRapport := false;


        end;


        if Element.Position < 0 then begin
           // L'élément a été ajouté en cours de cette session
           // Ajoute à la fin du stream
           s.Seek(0, soEnd);

           Element.Position := s.Position;
           SaveFixedData(s, Element);
           Element.bShouldBeUpdateFixed := false;

        end else begin
           // Position >= 0

           if Element.bShouldBeUpdateFixed then begin
              s.Seek(Element.Position, soBeginning);

              SaveFixedData(s, Element);

              Element.bShouldBeUpdateFixed := false;
           end;

        end;

    end;


    FData.Free;

    // Résultat
    Result := Count;

end;

function TCollectionRapportHead.LoadFromFile(Fichier: string): integer;
var F: TFileStream;
begin

    FGalaxie := -1;
    FUnivers := -1;

    if Fichier = '' then Fichier := FFichierHead;
    FFichierDB := Fichier + '.d';

    // ATTENTION: Les éléments sont effacés avant le chargement, à modifier
    // en fonction de ce que l'on veut
    Clear;

    Result  := -1;
    Fichier := sfcUnixPathToDosPath(Fichier);
    FFichierHead := Fichier;
    if FileExists(Fichier) = false then exit;


    try
       F := TFileStream.Create(Fichier, fmOpenRead);
    except
       F.Free;
       F := nil;
    end;

    // IOResult renvoie 0 => cela signifie que la dernière opération d'E/S a réussi
    if F = nil then begin
       exit;
    end;

    Result := LoadFromStream(F);


    // Fermeture du fichier
    F.Free;

end;


function TCollectionRapportHead.SaveToFile(Fichier: string): integer;
var i: integer;
    F: TFileStream;
begin

    Result := -2;

    if Fichier = '' then Fichier := FFichierHead;
    if Fichier = '' then begin
       Result := -18;
       exit;
    end;


    if FDBUpdating then begin
       if FileExists(Fichier) then
          DeleteFile(Fichier);

       // Le fichier n'existant plus, on peut enlever le flag (obligé)
       FDBUpdating := false;
    end;


    try

      if FileExists(Fichier) then
         F := TFileStream.Create(Fichier, fmOpenWrite)
      else
         F := TFileStream.Create(Fichier, fmCreate);


    except
      F.Free;
      F := nil;
    end;

    // IOResult renvoie 0 => cela signifie que la dernière opération d'E/S a réussi
    if F = nil then begin
       exit;
    end;

    Result := SaveToStream(F);


    // Ferme le fichier
    F.Free;
    
end;

constructor TCollectionRapportHead.Create;
begin

    // Héritage
    InHerited Create;
    
    
    
    // Code pour l'initialisation ici
    FUnivers := -1;
    FGalaxie := -1;

    FLoadOnlyHeader := false;
    FDBUpdating := false;

    FFichierHead := '';
    FFichierDB   := '';

end;

destructor TCollectionRapportHead.Destroy;
begin
    // Implémenter le code avant l'héritage de la méthode
    // C'est à dire ici
    
   
    // On appelle NOTRE clear qui lui-même appelle NOTRE Delete qui libère l'espace mémoire
    // avant d'enlever le pointeur de la TList
    // L'héritage de Destroy appelle Clear, mais es-ce le notre ou celui de la classe de base ?
    Clear;
    
    
    // Héritage
    InHerited Destroy;
end;


procedure TCollectionRapportHead.Clear;
var i: integer;
begin

    if Count > 0 then begin
        For i := Count -1 downto 0 do
            Delete(i);
    end;
    
    inherited Clear;
end;

function TCollectionRapportHead.ExistSomeDataToSave: boolean;
var pe: PRapportHead;
    i : integer;
begin

   Result := false;

   if Count = 0 then exit;

   for i := 0 to Count - 1 do begin
       pe := Items[i];

       if (pe.bShouldBeUpdateFixed) or (pe.bShouldBeUpdateRapport) then begin
          // Il reste des données à sauvegarder
          Result := true;
          exit;
       end;


   end;

end;

function TCollectionRapportHead.Get_Pointeur(Element: TPlanetLocation): PRapportHead;
var i: integer;
begin

   Result := nil;
   
   i := Get_Index(Element);
   if i < 0 then exit;
   Result := Items[i];

end;

function TCollectionRapportRecent.Get_Pointeur(Element: TPlanetLocation): PRapportHead;
var i: integer;
begin

    if Count = 0 then begin
       Result := nil;
       exit;
    end;


    for i := 0 to Count - 1 do begin
        Result := Items[i];

      if CompareLocation(Result.PlaneteLocation, Element) then begin
         exit;
      end;

    end;

    Result := nil;

end;

function TCollectionRapportHead.Get_Index(Element: TPlanetLocation): integer;
var i: integer;
    pElement: PRapportHead;
begin

   Result := -1;
   if Count <= 0 then exit;

   for i := 0 to Count - 1 do begin
      pElement := Items[i];

      if CompareLocation(pElement.PlaneteLocation, Element) then begin
         Result := i;
         exit;
      end;
   end;

end;

function TCollectionRapportHead.Add_Update(Element: TRapportHead): PRapportHead;
var i : integer;
begin

     Result := nil;
                                         {### ### ### ### ### ### ### ### ### ### ### ### ### ### }
     i := Get_Index(Element.PlaneteLocation);        {### Neccessite adpation en fonction du type record  ### }
                                         {### ### ### ### ### ### ### ### ### ### ### ### ### ### }
     if i < 0 then begin 
        // L'élément n'existe pas, l'ajoute
        Result  := Add(Element);
     end else begin
        // L'élément existe déjà, le met à jour
        Result  := Items[i];     // Obtient le pointeur
        Result^ := Element;
     end;
     
end;




function TCollectionRapportHead.Get_Item(Index: integer): TRapportHead;
var pElement: PRapportHead;
begin

     if Index < 0 then exit;
     if Index >= Count then exit;

     pElement := Items[Index];     // Obtient le pointeur
     Result := pElement^;

end;

function TCollectionRapportHead.Delete(Index: integer): boolean;
var pElement: PRapportHead;
begin

    Result := false;

    if (Index < 0) or (Index >= Count) then
       // Hors limite
       exit;


    // Obtient le pointeur
    pElement := Items[Index];

    if pElement <> nil then
       // Libére la mémoire créée lors de l'ajout
       Dispose(pElement);

    // Supprime le pointeur de la liste
    inherited Delete(Index);

    // Met le resultat à True
    Result := true;

end;

function TCollectionRapportHead.Delete(Element: TPlanetLocation): boolean;
var i : integer;
begin

    Result := false;
    i      := Get_Index(Element);

    if i >= 0 then begin
       // Element trouvé
       // Le supprime
       Delete(i);

       // Même si la suppression Echoue on renvoi True
       // Car l'élément a été trouvé
       Result := true;
    end;
end;


function TCollectionRapportHead.AddUnknowFromCoord(Element: TPlanetLocation): PRapportHead;
var e: TRapportHead;
    i: integer;
begin

    e.PlaneteLocation.Galaxie       := Element.Galaxie;
    e.PlaneteLocation.SystemSolaire := Element.SystemSolaire;
    e.PlaneteLocation.Position      := Element.Position;

    e.Ressource.Metal     := 0;
    e.Ressource.Cristal   := 0;
    e.Ressource.Deuterium := 0;
    e.Ressource.Energie   := 0;


    for i := 0 to MAX_INDICE_HISTORIQUE do
       e.DateLastAttaque[i] := -1;

    for i := 0 to MAX_INDICE_HISTORIQUE do
       e.VieuxRapport[i] := -1;

    for i := 0 to MAX_INDICE_HISTORIQUE do
       e.DateLastEspionnage[i] := -1;


    // Nombre de fois espionné
    e.NbEspionnage    := 0;
    e.NbAttack        := 0;

    // Position dans le fichier, qui sera définit lors de la sauvegarde
    e.Position := -1;
    e.PositionRapport := -1;

    // il faudra bien sur mettre à jour les données
    e.bShouldBeUpdateFixed   := true;
    e.bShouldBeUpdateRapport := true;

    e.RapportFullOri := 'Inconnu - Directement attaqué';

    e.Flags := 0;

    e.RecyclageNbRessource := 0;
    e.DefenseScore     := 0;

    e.NbDefense := 0;
    e.NbFlotte  := 0;

    e.SectionConnu := [];
    e.PourcentContreEspion := 0;

    
    Result := Add(e);


end;

function TCollectionRapportHead.Add(Element: TRapportHead): PRapportHead;
var pElement: PRapportHead;
begin
    
    Result := nil;
    
    New(pElement);          // Créer l'espace mémoire requis
    pElement^ := Element;   // Recopie les données dans l'emplacement pointé
    
    try
       inherited Add(pElement);  // Ajoute le pointeur à la liste
       Result := pElement;
    except
       Result := nil;
    end;
    
    // Pour libérer la mémoire occupé par l'élément pointer:  Dispose(pElement);

end;

// Simple ajout de pointeur
procedure TCollectionRapportHead.SimpleAdd(Pointeur: PRapportHead);
begin
    
    inherited Add(Pointeur);  // Ajoute le pointeur à la liste
    
end;

function TCollectionRapportHead.GetItemx(Index: integer): TRapportHead;
var pElement: PRapportHead;
begin

   if Count < 0 then exit;
   if Index >= Count then exit;

   pElement := Items[Index];
   Result := pElement^;

end;


end.

